package com.group.project.hrms.controller;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.group.project.hrms.entity.*;
import com.group.project.hrms.entity.ResultEntity.ResultEntity;
import com.group.project.hrms.service.*;
import com.group.project.hrms.utils.*;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.util.*;

@Slf4j
@Controller
public class SalaryMonthlyReportController {

    private static Logger logger = LoggerFactory.getLogger(SalaryMonthlyReportController.class);

    @Autowired
    private ISalaryMonthlyReportService iSalaryMonthlyReportService;

    @Autowired
    private ISalaryBaseAllowanceRewardService iSalaryBaseAllowanceRewardService;

    @Autowired
    private ISalaryBaseBasicService iSalaryBaseBasicService;

    @Autowired
    private ISalaryBaseWithHoldService iSalaryBaseWithHoldService;

    @Autowired
    private IStandardBonusFullAttendanceService iStandardBonusFullAttendanceService;

    @Autowired
    private IStandardBonusWorkingYearsService iStandardBonusWorkingYearsService;

    @Autowired
    private IStandardBaseAttendChargeBackService iStandardBaseAttendChargeBackService;

    @Autowired
    private IStandardBaseAttendExtraHoursService iStandardBaseAttendExtraHoursService;

    @Autowired
    private SalaryUtil salaryUtil;

    @Autowired
    private AttendanceUtil attendanceUtil;

    @Autowired
    private DateUtil dateUtil;

    @Autowired
    private IContactBasicService iContactBasicService;

    @Autowired
    private IContactBasicDeptInfoService iContactBasicDeptInfoService;

    @Autowired
    private IAttendanceMonthlyReportService iAttendanceMonthlyReportService;

    @Autowired
    private IContactArchivesWorkingService iContactArchivesWorkingService;

    @Autowired
    private LogUtil logUtil;

    @Autowired
    private ISpecialCheckDateService iSpecialCheckDateService;

    @Autowired
    private IAttendanceGroupInfoService iAttendanceGroupInfoService;

    @Autowired
    private IHolidayRecordService iHolidayRecordService;


    /**
     * 接收前端请求，计算月度薪资
     * @param map 需要计算薪资的月份
     * @param request http请求
     * @return 标准返回值对象
     */
    @RequestMapping("/basic/doGetSalaryMonthlyReport")
    @ResponseBody
    public ResultEntity doGetSalaryMonthlyReport(@RequestBody Map map, HttpServletRequest request){

        //创建日志信息
        Date date = new Date();
        Object staffName = request.getAttribute("staffName");

        //创建返回值结果对象
        ResultEntity resultEntity = new ResultEntity();
        List<Map> resultList = new ArrayList<>();
        Map<String, List> resultMap = new HashMap<>();

        //检验传参是否正确，若正确，则获取用户指定的月份
        logger.info("用户在 " + date + " 发起了薪资计算请求，开始检查传入参数是否正确......");
        if(null == map || null == map.get("month")){
            resultEntity.setCode("F");
            resultEntity.setMsg("参数不正确");
            return resultEntity;
        }
        Object month = map.get("month");
        logger.info("参数正确，正在计算的月份是：" + month);

        //检查该月份是否已经归档
        List<SalaryMonthlyReport> archivedList = iSalaryMonthlyReportService.selectSalaryMonthlyReportByFileMonth(month.toString());
        if(archivedList.size() != 0){
            resultEntity.setCode("F");
            resultEntity.setMsg("该月份已经归档");
            //resultEntity.setDataList(reportList);
            return resultEntity;
        }

        //将通讯录和部门信息加载到结果集
        resultMap.put("contactList", iContactBasicService.selectAllContactBasic());
        resultMap.put("deptList", iContactBasicDeptInfoService.selectAllContactBasicDeptInfo());

        //------------------加载基础数据---------------------
        logger.info("加载薪资数据......");

        //加载薪资数据
        List<SalaryBaseBasic> salaryBaseBasicList = iSalaryBaseBasicService.selectSalaryBaseBasicByMonth(month.toString());
        List<SalaryBaseWithHold> salaryBaseWithHoldList = iSalaryBaseWithHoldService.selectSalaryBaseWithHoldByMonth(month.toString());
        List<SalaryBaseAllowanceReward> salaryBaseAllowanceRewardList = iSalaryBaseAllowanceRewardService.selectSalaryBaseAllowanceRewardByMonth(month.toString());

        //加载薪资计算标准
        List<StandardBonusFullAttendance> standardBonusFullAttendanceList = iStandardBonusFullAttendanceService.selectAllStandardBonusFullAttendance();
        List<StandardBonusWorkingYears> standardBonusWorkingYearsList = iStandardBonusWorkingYearsService.selectAllStandardBonusWorkingYears();
        List<StandardBaseAttendChargeBack> standardBaseAttendChargeBackList = iStandardBaseAttendChargeBackService.selectAllStandardBaseAttendChargeBack();
        List<StandardBaseAttendExtraHours> standardBaseAttendExtraHoursList = iStandardBaseAttendExtraHoursService.selectAllStandardBaseAttendExtraHours();

        //加载入职和离职信息对象
        List<ContactArchivesWorking> archivesWorkings = iContactArchivesWorkingService.selectAllContactArchivesWorking();

        //加载当月入职信息对象
        List<ContactArchivesWorking> joinArchivesWorkings = iContactArchivesWorkingService.selectContactArchivesWorkingsByJoinedMonth(month.toString());

        //加载当月离职信息对象
        List<ContactArchivesWorking> leaveArchivesWorkings = iContactArchivesWorkingService.selectContactArchivesWorkingsByLeaveMonth(month.toString());

        //加载考勤归档数据
        List<AttendanceMonthlyReport> attendanceMonthlyReports = iAttendanceMonthlyReportService.selectAttendanceMonthlyReportByArchiveMonth(month.toString());

        //获取需计算月份的所有特殊打卡日及所有考勤组对象
        List<SpecialCheckDate> specialCheckDateList = iSpecialCheckDateService.selectSpecialCheckDateBySPMonth(month.toString());
        List<AttendanceGroupInfo> attendanceGroupInfoList = iAttendanceGroupInfoService.selectAllAttendanceGroupInfo();

        //加载本月日历信息和本月节假日信息
        int maxDays;
        List<Integer> weekendList;
        try{
            Calendar calendar = dateUtil.getCalendarByMonth(month.toString());
            maxDays = calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
            weekendList = dateUtil.getWeekendListByMonth(calendar);
        }catch (Exception e){
            resultEntity.setCode("F");
            resultEntity.setMsg("程序在计算当月时间时出现异常");
            return resultEntity;
        }
        List<HolidayRecord> holidayRecordList = iHolidayRecordService.selectHolidayRecordByMonth(month.toString());


        //-------------------校验并预处理基础数据---------------------
        logger.info("校验并预处理基础数据......");
        if(null == salaryBaseBasicList || salaryBaseBasicList.size() == 0){
            resultEntity.setCode("F");
            resultEntity.setMsg("请先上传该月份基础薪资表");
            return resultEntity;
        }
        if(null == attendanceMonthlyReports || attendanceMonthlyReports.size() == 0){
            resultEntity.setCode("F");
            resultEntity.setMsg("请先归档该月份的考勤数据");
            return resultEntity;
        }

        //获取本月实际工作日数组
        List<Integer> actWorkingDays = new ArrayList<>();
        for (int i = 1; i <= maxDays; i++) {
            int checkFlag = dateUtil.checkHoliday(holidayRecordList, i);
            if(2 == checkFlag || (!dateUtil.isWeekend(weekendList, i) && 1 != checkFlag && 3 != checkFlag)){
                actWorkingDays.add(i);
            }
        }
        logger.info("当月实际工作日(不含特殊打卡日和特殊不打卡日)为：" + actWorkingDays);

        Map<String, SalaryBaseWithHold> salaryBaseWithHoldMap = salaryUtil.getSalaryBaseWithHoldMap(salaryBaseWithHoldList); //行政每月扣款和代扣费用
        Map<String, SalaryBaseAllowanceReward> salaryBaseAllowanceRewardMap = salaryUtil.getSalaryBaseAllowanceRewardMap(salaryBaseAllowanceRewardList); //月度补贴和奖励明细
        Map<String, ContactArchivesWorking> contactArchivesWorkingMap = attendanceUtil.getContactWorkingMap(archivesWorkings); //所有员工的离职入职信息
        Map<String, AttendanceMonthlyReport> attendanceMonthlyReportMap = salaryUtil.getAttendanceMonthlyReportMap(attendanceMonthlyReports); //考勤月度报表
        Map<String, SpecialCheckDate> specialCheckDateMap = attendanceUtil.getSpecialCheckDateMap(specialCheckDateList); //所要计算的月份的所有特殊打卡或者不打卡日的Map集合
        Map<String, AttendanceGroupInfo> attendanceGroupInfoMap = attendanceUtil.getAttendanceGroupInfoMap(attendanceGroupInfoList); //所有考勤组Map
        Map<String, ContactArchivesWorking> joinArchivesWorkingMap = attendanceUtil.getContactWorkingMap(joinArchivesWorkings); //当月入职人员信息
        Map<String, ContactArchivesWorking> leaveArchivesWorkingMap = attendanceUtil.getContactWorkingMap(leaveArchivesWorkings); //当月离职人员信息

        Map<String, StandardBonusFullAttendance> bonusFullAttendanceMap = salaryUtil.getStandardBonusFullAttendanceMap(standardBonusFullAttendanceList); //全勤奖标准
        Map<String, StandardBaseAttendChargeBack> baseAttendChargeBackMap = salaryUtil.getStandardBaseAttendChargeBackMap(standardBaseAttendChargeBackList); //考勤扣款计算基数项
        Map<String, StandardBaseAttendExtraHours> baseAttendExtraHoursMap = salaryUtil.getStandardBaseAttendExtraHoursMap(standardBaseAttendExtraHoursList); //加班费计算基数项


        //开始计算 - 循环基本工资表(List<SalaryBaseBasic>)
        logger.info("开始计算 - 循环基本工资表......");
        List<SalaryMonthlyReport> salaryMonthlyReportList = new ArrayList<>();
        for (SalaryBaseBasic sbb : salaryBaseBasicList
             ) {
            //创建月度工资报表对象
            SalaryMonthlyReport userSalaryMonthlyReport = new SalaryMonthlyReport(new BigDecimal(0));
            userSalaryMonthlyReport.setTableId(UUIDUtil.createUUID()); //设置报表ID
            userSalaryMonthlyReport.setcTime(date); //设置生成日期
            userSalaryMonthlyReport.setFileMonth(month.toString()); //设置归档月份标志

            String contactID = sbb.getContactId();
            userSalaryMonthlyReport.setContactId(contactID); //设置联系人ID

            AttendanceMonthlyReport attendanceMonthlyReport = attendanceMonthlyReportMap.get(contactID);
            if(null != attendanceMonthlyReport){ //有考勤才计算工资，无考勤，则直接使用薪资报表初始化为0的报表

                String attendGroupID =  attendanceMonthlyReport.getGroupId(); //获取该员工的考勤组ID
                String attendReportID = attendanceMonthlyReport.getReportId(); //考勤报表ID
                userSalaryMonthlyReport.setAttendReportId(attendReportID); //设置关联的考勤报表ID

                BigDecimal initValue = new BigDecimal(0); //初始化值 备用

                //一、获取该员工用于计算薪资的基础数据

                //1，根据考勤组ID，获取全勤奖对象
                StandardBonusFullAttendance bonusFullAttendance = bonusFullAttendanceMap.get(attendGroupID);

                //2，获取考勤报表中的休假数据
                float casualLeaveHours = attendanceMonthlyReport.getCasualLeaveHours(); //事假
                float medicalLeaveHours = attendanceMonthlyReport.getMedicalLeaveHours(); //病假
                float annualLeaveHours = attendanceMonthlyReport.getAnnualLeaveHours(); //年假
                float maternityLeaveHours = attendanceMonthlyReport.getMaternityLeaveHours(); //产假
                float marriageLeaveHours = attendanceMonthlyReport.getMarriageLeaveHours(); //婚假
                float bereavementLeaveHours = attendanceMonthlyReport.getBereavementLeaveHours(); //丧假
                float exchangeLeaveHours = attendanceMonthlyReport.getExchangeLeaveHours(); //调休
                float othersLeaveHours = attendanceMonthlyReport.getOthersLeaveHours(); //其它假

                //3，获取基本工资信息项的值
                BigDecimal basicSalary = (sbb.getBasicSalary() == null) ? initValue : sbb.getBasicSalary();
                BigDecimal postSalary = (sbb.getPostSalary() == null) ? initValue : sbb.getPostSalary();
                BigDecimal basicOvertimeAllowance = (sbb.getBasicOvertimeAllowance() == null) ? initValue : sbb.getBasicOvertimeAllowance();
                BigDecimal managementAchievements = (sbb.getManagementAchievements() == null) ? initValue : sbb.getManagementAchievements();
                BigDecimal produceAchievements = (sbb.getProduceAchievements() == null) ? initValue : sbb.getProduceAchievements();
                BigDecimal confidentialityFee = (sbb.getConfidentialityFee() == null) ? initValue : sbb.getConfidentialityFee();

                //4，根据考勤组ID，获取加班费和考勤扣款的计算基数
                String chargeBackArray = (baseAttendChargeBackMap.get(attendGroupID) == null) ? null : baseAttendChargeBackMap.get(attendGroupID).getAttendChargeBackBase();
                String extraHoursArray = (baseAttendExtraHoursMap.get(attendGroupID) == null) ? null : baseAttendExtraHoursMap.get(attendGroupID).getAttendExtraHoursBase();

                BigDecimal baseAttendExtraHoursStandard =  new BigDecimal(0); //定义加班费计算基数项之和的金额并初始化
                BigDecimal baseAttendChargeBackStandard = new BigDecimal(0); //定义考勤扣款基数项之和的金额并初始化

                for (int count = 0; count < 2; count++) {
                    String tempStr;
                    if(count == 0){
                        tempStr = chargeBackArray;
                    }else {
                        tempStr = extraHoursArray;
                    }
                    if(null != tempStr){
                        JSONArray array = JSON.parseArray(tempStr);
                        if(null != array && array.size() != 0){
                            for (int i = 0; i < array.size(); i++) {
                                String flag = array.getString(i);
                                if(flag.equals("A")){ //表示包含基本工资
                                    if(count == 0){
                                        baseAttendChargeBackStandard = baseAttendChargeBackStandard.add(basicSalary);
                                    }else {
                                        baseAttendExtraHoursStandard = baseAttendExtraHoursStandard.add(basicSalary);
                                    }
                                }
                                if(flag.equals("B")){ //表示包含岗位工资
                                    if(count == 0){
                                        baseAttendChargeBackStandard = baseAttendChargeBackStandard.add(postSalary);
                                    }else {
                                        baseAttendExtraHoursStandard = baseAttendExtraHoursStandard.add(postSalary);
                                    }
                                }
                                if(flag.equals("C")){ //表示包含管理绩效
                                    if(count == 0){
                                        baseAttendChargeBackStandard = baseAttendChargeBackStandard.add(managementAchievements);
                                    }else {
                                        baseAttendExtraHoursStandard = baseAttendExtraHoursStandard.add(managementAchievements);
                                    }
                                }
                                if(flag.equals("D")){ //表示包含生产绩效
                                    if(count == 0){
                                        baseAttendChargeBackStandard = baseAttendChargeBackStandard.add(produceAchievements);
                                    }else {
                                        baseAttendExtraHoursStandard = baseAttendExtraHoursStandard.add(produceAchievements);
                                    }
                                }
                                if(flag.equals("E")){ //表示包含保密费
                                    if(count == 0){
                                        baseAttendChargeBackStandard = baseAttendChargeBackStandard.add(confidentialityFee);
                                    }else {
                                        baseAttendExtraHoursStandard = baseAttendExtraHoursStandard.add(confidentialityFee);
                                    }
                                }
                                if(flag.equals("F")){ //表示包含保密费
                                    if(count == 0){
                                        baseAttendChargeBackStandard = baseAttendChargeBackStandard.add(basicOvertimeAllowance);
                                    }else {
                                        baseAttendExtraHoursStandard = baseAttendExtraHoursStandard.add(basicOvertimeAllowance);
                                    }
                                }
                            }
                        }
                    }
                }

                //5，创建被除数基数对象 21.75是中国国家法定的计算日薪的被除数
                //BigDecimal monthDay = new BigDecimal(21.75); 暂时不使用天来计算考勤扣款
                BigDecimal hourDay = new BigDecimal(21.75 * 8);

                //6，根据基数总额，计算并获取考勤扣款的每小时费用 ROUND_HALF_UP - 四舍五入 并保留四位小数
                BigDecimal perHourFeeForAttendChargeBack = baseAttendChargeBackStandard.divide(hourDay, 2, BigDecimal.ROUND_HALF_UP);

                //7，根据基数总额，计算并获取加班费的每小时费用 ROUND_HALF_UP - 四舍五入 并保留四位小数
                BigDecimal perHourFeeForAttendExtraHours = baseAttendExtraHoursStandard.divide(hourDay, 2, BigDecimal.ROUND_HALF_UP);

                //8，获取高温补贴、夜班补助、其它补助、其它奖励
                SalaryBaseAllowanceReward baseAllowanceReward = salaryBaseAllowanceRewardMap.get(contactID);

                //9，计算当月离职人员和入职人员的缺勤天数
                List<Integer> nonAttendDays = new ArrayList<>(); //总的缺勤天数的数组（含当月入职和离职所产生的缺勤）
                List<Integer> spNeedChecks = new ArrayList<>(); //本月特殊打卡日数组
                List<Integer> spNotNeedChecks = new ArrayList<>(); //本月特殊不打卡日数组
                //获取考勤组下特殊打卡日和特殊不打卡日的ID，根据ID查询Map，最终获取特殊打卡日与不打卡日数组
                String spIDs = attendanceGroupInfoMap.get(attendGroupID).getSpecialCheckIds(); //如果存在考勤报表，则一定存在考勤组，但考勤组的特殊日ID有可能为空
                if(null != spIDs){
                    JSONArray ids = JSON.parseArray(spIDs);
                    if(null != ids && ids.size() != 0){
                        for (Object spid : ids
                        ) {
                            SpecialCheckDate specialCheckDate = specialCheckDateMap.get(spid);
                            if(null != specialCheckDate){
                                int spDay = dateUtil.getDayByDate(specialCheckDate.getSpecialDate());
                                if(specialCheckDate.getCheckFlag().equals("B")){ //打卡标志为B, 不打卡标志为A
                                    spNeedChecks.add(spDay);
                                }
                                if(specialCheckDate.getCheckFlag().equals("A")){
                                    spNotNeedChecks.add(spDay);
                                }
                            }
                        }
                    }
                }
                //计算当月入职缺勤
                ContactArchivesWorking joinWorking = joinArchivesWorkingMap.get(contactID);
                if(null != joinWorking){ //说明这个人存在当月入职的情况
                    int joinDate = dateUtil.getDayByDate(joinWorking.getOnboardActualDate());
                    for (int i = 1; i < joinDate; i++) { //循环在入职日之前的日期，如果是工作日或者特殊打卡日，且不能为特殊不打卡日，就算缺勤一天
                        if((actWorkingDays.contains(i) || spNeedChecks.contains(i)) && !spNotNeedChecks.contains(i)){
                            nonAttendDays.add(i);
                        }
                    }
                }
                ContactArchivesWorking leaveWorking = leaveArchivesWorkingMap.get(contactID);
                if(null != leaveWorking){ //说明这个人存在当月离职的情况
                    int leaveDate = dateUtil.getDayByDate(leaveWorking.getLeaveActualDate());
                    for (int i = leaveDate + 1; i <= maxDays; i++) { //循环在离职日之后的日期，如果是工作日或者特殊打卡日，且不能为特殊不打卡日，就算缺勤一天
                        if((actWorkingDays.contains(i) || spNeedChecks.contains(i)) && !spNotNeedChecks.contains(i)){
                            nonAttendDays.add(i);
                        }
                    }
                }


                //二、设置工资报表中的项

                //设置基本工资、岗位工资、加班补贴、管理绩效、生产绩效、保密费
                userSalaryMonthlyReport.setBasicSalary(basicSalary);
                userSalaryMonthlyReport.setPostSalary(postSalary);
                userSalaryMonthlyReport.setBasicOvertimeAllowance(basicOvertimeAllowance);
                userSalaryMonthlyReport.setManagementAchievements(managementAchievements);
                userSalaryMonthlyReport.setProduceAchievements(produceAchievements);
                userSalaryMonthlyReport.setConfidentialityFee(confidentialityFee);

                //设置全勤奖 - 如果没有休假，且该考勤组设置了全勤奖，就设置该项 （+ exchangeLeaveHours 调休除外）
                float totalLeaveHours = casualLeaveHours + medicalLeaveHours + annualLeaveHours + maternityLeaveHours + marriageLeaveHours + bereavementLeaveHours + othersLeaveHours;
                if(null != bonusFullAttendance){
                    if(totalLeaveHours == 0 && attendanceMonthlyReport.getNeglectWorkCount() == 0){ //未请假未旷工，可享受全勤奖
                        userSalaryMonthlyReport.setFullAttendanceBonus(bonusFullAttendance.getStandard());
                    }
                }

                //查询工龄，循环工龄奖设置集合，设置该员工的工龄奖，同时，根据入职和离职日期在本月的情况，计算缺勤天数
                ContactArchivesWorking userArchivesWorking = contactArchivesWorkingMap.get(contactID);
                if(null != userArchivesWorking){
                    Date joinDate = userArchivesWorking.getOnboardActualDate(); //获取入职日期
                    if(null != joinDate){ //根据工龄，获取工龄奖
                        int workYears = dateUtil.getWorkAge(date, joinDate);
                        BigDecimal lengthServiceBonus = null;
                        if(null != standardBonusWorkingYearsList && standardBonusWorkingYearsList.size() != 0){
                            int casualYear = 0;
                            for (StandardBonusWorkingYears sbwy : standardBonusWorkingYearsList
                                 ) {
                                if(attendGroupID.equals(sbwy.getAttendGroupId())){
                                    if(workYears >= sbwy.getWorkingYears() && sbwy.getWorkingYears() > casualYear){
                                        casualYear = sbwy.getWorkingYears();
                                        lengthServiceBonus = sbwy.getStandard();
                                    }
                                }
                            }
                        }
                        if(null != lengthServiceBonus){
                            userSalaryMonthlyReport.setLengthServiceBonus(lengthServiceBonus);
                        }
                    }
                }


                //设置平时、周末、节日的加班费
                userSalaryMonthlyReport.setWorkingDayAddFee(perHourFeeForAttendExtraHours.multiply(new BigDecimal(attendanceMonthlyReport.getDailyOvertimeHours()).multiply(new BigDecimal(1.5))));
                userSalaryMonthlyReport.setWeekendAddFee(perHourFeeForAttendExtraHours.multiply(new BigDecimal(attendanceMonthlyReport.getWeekendOvertimeHours()).multiply(new BigDecimal(2.0))));
                userSalaryMonthlyReport.setHolidayAddFee(perHourFeeForAttendExtraHours.multiply(new BigDecimal(attendanceMonthlyReport.getHolidayOvertimeHours()).multiply(new BigDecimal(3.0))));

                //设置高温补贴、夜班补助、其它补助(含备注)、其它奖励(含备注)
                if(null != baseAllowanceReward){
                    userSalaryMonthlyReport.setAllowanceHot(baseAllowanceReward.getAllowanceHot());
                    userSalaryMonthlyReport.setAllowanceNightShift(baseAllowanceReward.getAllowanceNightShift());
                    userSalaryMonthlyReport.setAllowanceOthers(baseAllowanceReward.getAllowanceOthers());
                    userSalaryMonthlyReport.setAllowanceOthersNote(baseAllowanceReward.getAllowanceOthersNote());
                    userSalaryMonthlyReport.setRewardOthers(baseAllowanceReward.getRewardOthers());
                    userSalaryMonthlyReport.setRewardOthersNote(baseAllowanceReward.getRewardOthersNote());
                }

                //计算并设置考勤扣款 - 扣款的考勤类型主要有事假和病假、以及计算月份入职与离职所产生的缺勤、旷工   其中病假先全部扣除，再计算补贴金额后加入到工资合计中

                //1)事假扣款 - 附加：将由于入职与离职所产生的缺勤、旷工小时数并入事假小时数，即合并计算扣款
                float totalAttendChargeBackHours = casualLeaveHours + (attendanceMonthlyReport.getNeglectWorkCount() * 8) + (nonAttendDays.size() * 8);
                BigDecimal casualLeaveHoursFee = perHourFeeForAttendChargeBack.multiply(new BigDecimal(totalAttendChargeBackHours));


                //2)病假扣款 - 先按事假全部扣除，再计算病假补贴后加入工资合计中
                BigDecimal medicalLeaveHoursFee = perHourFeeForAttendChargeBack.multiply(new BigDecimal(attendanceMonthlyReport.getMedicalLeaveHours()));
                BigDecimal perHourFeeForOnlyBasicSalary = basicSalary.divide(hourDay, 2, BigDecimal.ROUND_HALF_UP); //按照基本工资项计算出的时薪

                BigDecimal medicalHoursAllowanceFee = perHourFeeForOnlyBasicSalary.multiply(new BigDecimal(attendanceMonthlyReport.getMedicalLeaveHours())).multiply(new BigDecimal(0.8));

                //3)设置考勤扣款 = 事假扣款(全扣) + 病假扣款(全扣) - 病假补贴
                userSalaryMonthlyReport.setAttendanceHoursChargeBack(casualLeaveHoursFee.add(medicalLeaveHoursFee).subtract(medicalHoursAllowanceFee));

                //计算并设置工资合计 - 即：基本工资+岗位工资+加班补贴+管理绩效+生产绩效+全勤奖+工龄奖+保密费+平时加班费+周末加班费+节日加班费+高温补贴+夜班补助+其它补贴+其它奖励-考勤扣款(先扣掉事假和病假，再加上病假补贴，其它假不扣款)
                BigDecimal totalSalary = userSalaryMonthlyReport.getBasicSalary()
                        .add(userSalaryMonthlyReport.getPostSalary())
                        .add(userSalaryMonthlyReport.getBasicOvertimeAllowance())
                        .add(userSalaryMonthlyReport.getManagementAchievements())
                        .add(userSalaryMonthlyReport.getProduceAchievements())
                        .add(userSalaryMonthlyReport.getFullAttendanceBonus())
                        .add(userSalaryMonthlyReport.getLengthServiceBonus())
                        .add(userSalaryMonthlyReport.getConfidentialityFee())
                        .add(userSalaryMonthlyReport.getWorkingDayAddFee())
                        .add(userSalaryMonthlyReport.getWeekendAddFee())
                        .add(userSalaryMonthlyReport.getHolidayAddFee())
                        .add(userSalaryMonthlyReport.getAllowanceHot())
                        .add(userSalaryMonthlyReport.getAllowanceNightShift())
                        .add(userSalaryMonthlyReport.getAllowanceOthers())
                        .add(userSalaryMonthlyReport.getRewardOthers())
                        .subtract(userSalaryMonthlyReport.getAttendanceHoursChargeBack());

                userSalaryMonthlyReport.setTotalSalary(totalSalary);

                //设置行政扣款、行政扣款备注、社保代扣、公积金代扣、个税代扣、水电代扣、住宿费代扣、其它代扣、其它代扣备注
                SalaryBaseWithHold salaryBaseWithHold = salaryBaseWithHoldMap.get(contactID);
                if(null != salaryBaseWithHold){
                    userSalaryMonthlyReport.setAdminChargeBack(salaryBaseWithHold.getAdminChargeBack());
                    userSalaryMonthlyReport.setAdminChargeBackNote(salaryBaseWithHold.getAdminChargeBackNote());
                    userSalaryMonthlyReport.setWithHoldInsurance(salaryBaseWithHold.getWithHoldInsurance());
                    userSalaryMonthlyReport.setWithHoldFund(salaryBaseWithHold.getWithHoldFund());
                    userSalaryMonthlyReport.setWithHoldIncomeTax(salaryBaseWithHold.getWithHoldIncomeTax());
                    userSalaryMonthlyReport.setWithHoldLife(salaryBaseWithHold.getWithHoldLife());
                    userSalaryMonthlyReport.setWithHoldHomestay(salaryBaseWithHold.getWithHoldHomestay());
                    userSalaryMonthlyReport.setWithHoldOthers(salaryBaseWithHold.getWithHoldOthers());
                    userSalaryMonthlyReport.setWithHoldOthersNote(salaryBaseWithHold.getWithHoldOthersNote());
                }

                //计算应发金额、实发金额并设置
                BigDecimal shouldPaySalary = totalSalary.subtract(userSalaryMonthlyReport.getAdminChargeBack());
                userSalaryMonthlyReport.setShouldPaySalary(shouldPaySalary);
                BigDecimal actualPaySalary = shouldPaySalary
                        .subtract(userSalaryMonthlyReport.getWithHoldInsurance())
                        .subtract(userSalaryMonthlyReport.getWithHoldFund())
                        .subtract(userSalaryMonthlyReport.getWithHoldIncomeTax())
                        .subtract(userSalaryMonthlyReport.getWithHoldLife())
                        .subtract(userSalaryMonthlyReport.getWithHoldHomestay())
                        .subtract(userSalaryMonthlyReport.getWithHoldOthers());

                userSalaryMonthlyReport.setActualPaySalary(actualPaySalary);

            }

            salaryMonthlyReportList.add(userSalaryMonthlyReport); //将每个人的计算结果存入返回值对象的集合中
        }
        logUtil.insertSystemEventLog(staffName.toString(), date, "计算了" + month + "月份的薪资报表", "薪资");
        logger.info("计算结束，共计生成了 " + salaryMonthlyReportList.size() + " 张薪资表，并返回结果集......");
        SalaryUtil.reportList = salaryMonthlyReportList; //缓存计算结果
        resultMap.put("SalaryMonthlyReportList", salaryMonthlyReportList);
        resultList.add(resultMap);
        resultEntity.setDataList(resultList);
        resultEntity.setCode("S");
        resultEntity.setMsg("Success");
        return resultEntity;
    }


    /**
     * 接收前端请求，归档薪资报表
     * @param map 前端传入的请求归档的月份
     * @param request http请求
     * @return 标准返回值对象
     */
    @RequestMapping("/basic/doFileSalaryMonthlyReport")
    @ResponseBody
    public ResultEntity doFileSalaryMonthlyReport(@RequestBody Map map, HttpServletRequest request){
        ResultEntity resultEntity = new ResultEntity();
        Date date = new Date();
        Object staffName = request.getAttribute("staffName");

        if(null != map){
            Object month = map.get("month");
            if(null != month && null != SalaryUtil.reportList && SalaryUtil.reportList.size() != 0){
                List<SalaryMonthlyReport> filedReports = iSalaryMonthlyReportService.selectSalaryMonthlyReportByFileMonth(month.toString());
                if(filedReports.size() != 0){
                    resultEntity.setCode("F");
                    resultEntity.setMsg("您指定月份的薪资报表已经归档");
                    return resultEntity;
                }
                List<SalaryMonthlyReport> salaryMonthlyReportList = SalaryUtil.reportList;
                for (SalaryMonthlyReport report : salaryMonthlyReportList
                     ) {
                    iSalaryMonthlyReportService.insertSalaryMonthlyReport(report);
                }
                logUtil.insertSystemEventLog(staffName.toString(), date, "归档了" + month + "月份的薪资报表", "薪资");
                SalaryUtil.reportList = new ArrayList<>();
                resultEntity.setCode("S");
                resultEntity.setMsg("Success");
                return resultEntity;
            }else {
                resultEntity.setCode("F");
                resultEntity.setMsg("请先计算您指定月份的薪资报表");
                return resultEntity;
            }
        }
        resultEntity.setCode("F");
        resultEntity.setMsg("归档失败");
        return resultEntity;
    }


    /**
     * 接收前端指定月份的解归档请求，进行解归档操作
     * @param map 请求解归档的月份
     * @param request http请求
     * @return 标准返回值对象
     */
    @RequestMapping("/basic/doUnFileSalaryMonthlyReport")
    @ResponseBody
    public ResultEntity doUnFileSalaryMonthlyReport(@RequestBody Map map, HttpServletRequest request){
        ResultEntity resultEntity = new ResultEntity();
        Date date = new Date();
        Object staffName = request.getAttribute("staffName");

        if(null != map){
            Object month = map.get("month");
            List<SalaryMonthlyReport> filedReports = iSalaryMonthlyReportService.selectSalaryMonthlyReportByFileMonth(month.toString());
            if(null == filedReports || filedReports.size() == 0){
                resultEntity.setCode("F");
                resultEntity.setMsg("该月份尚未归档，无需解归档");
                return resultEntity;
            }else {
                for (SalaryMonthlyReport report : filedReports
                     ) {
                    iSalaryMonthlyReportService.deleteSalaryMonthlyReport(report);
                }
                logUtil.insertSystemEventLog(staffName.toString(), date, "解归档了" + month + "月份的薪资报表", "薪资");
                resultEntity.setCode("S");
                resultEntity.setMsg("Success");
                return resultEntity;
            }
        }
        resultEntity.setCode("F");
        resultEntity.setMsg("解归档失败");
        return resultEntity;
    }


    /**
     * 按月份查询归档薪资报表
     * @param map 前端传入的月份
     * @param request http请求
     * @return 标准返回值对象
     */
    @RequestMapping("/basic/getFiledSalaryMonthlyReportByAppointMonth")
    @ResponseBody
    public ResultEntity getFiledSalaryMonthlyReportByAppointMonth(@RequestBody Map map, HttpServletRequest request){
        ResultEntity resultEntity = new ResultEntity();
        Date date = new Date();
        Object staffName = request.getAttribute("staffName");

        if(null != map){
            Object month = map.get("month");
            if(null != month){
                Map<String, List> resultMap = new HashMap<>();
                List<Map> resultList = new ArrayList<>();
                resultMap.put("contactList", iContactBasicService.selectAllContactBasic());
                resultMap.put("deptList", iContactBasicDeptInfoService.selectAllContactBasicDeptInfo());
                resultMap.put("monthlyReportList", iSalaryMonthlyReportService.selectSalaryMonthlyReportByFileMonth(month.toString()));
                resultList.add(resultMap);
                resultEntity.setDataList(resultList);
                logUtil.insertSystemEventLog(staffName.toString(), date, "查询了" + month + "月份的薪资报表数据", "薪资");
                resultEntity.setCode("S");
                resultEntity.setMsg("Success");
                return resultEntity;
            }
        }else {
            resultEntity.setCode("F");
            resultEntity.setMsg("传入参数错误");
            return resultEntity;
        }
        resultEntity.setCode("F");
        resultEntity.setMsg("查询失败");
        return resultEntity;
    }

}
